# -*- coding: utf-8 -*-

import json
from datetime import datetime, timedelta

from django.conf import settings
from django.views.decorators.http import require_GET
from pymongo import MongoClient

from async import async_job
from common.admin import db as user_db
from common.channel import admin_db as channel_db
from common.order import admin_db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.api import token_required
from common.utils.decorator import response_wrapper, mongo_wrapper
from common.utils.exceptions import ParamError
from common.utils.mg import parse_query, paginator
from common.utils.tz import local_now
from common.export.handler import export_data_start

_LOGGER = track_logging.getLogger(__name__)
mg = MongoClient(settings.MONGO_ADDR).paychannel_stat


@mongo_wrapper
def get_aggregate_param(query_dct):
    group = {'_id': None, 'total_amount': {'$sum': '$total'}, 'avg_rate': {'$avg': '$rate'}}
    match = {'$and': []}
    if 'start' in query_dct.keys():
        time_cond = {}
        d = json.loads(query_dct['start'])
        if d.get('$gte'):
            start = datetime.strptime(d['$gte'], '%Y-%m-%d %H:%M:%S')
            time_cond['$gte'] = start
        if d.get('$lt'):
            end = datetime.strptime(d['$lt'], '%Y-%m-%d %H:%M:%S')
            time_cond['$lt'] = end
        match['$and'].append({'start': time_cond})
    if 'mch_id' in query_dct.keys():
        if 'in' in str(query_dct['mch_id']):
            match['$and'].append({'mch_id': query_dct['mch_id']})
        else:
            match['$and'].append({'mch_id': int(query_dct['mch_id'])})

    if 'service' in query_dct.keys():
        match['$and'].append({'service_name': query_dct['service']})
    if 'channel_id' in query_dct.keys():
        match['$and'].append({'channel_id': query_dct['channel_id']})
    if 'channel_type' in query_dct.keys():
        match['$and'].append({'channel_type': query_dct['channel_type']})
    if len(match['$and']) > 0:
        items = mg.channel_status.aggregate([{'$match': match},
                                             {'$group': group},
                                             ])
    else:
        items = mg.channel_status.aggregate([{'$group': group}, ])
    total_amount = 0
    avg_rate = 0
    for r in items:
        total_amount = r.get('total_amount')
        avg_rate = r.get('avg_rate')
    return total_amount / 100.0, avg_rate


@mongo_wrapper
def list_channel_daily_statistic(query_dct):
    query_cond = parse_query(query_dct)
    if 'type' in query_cond:
        query_cond['type'] = int(query_cond['type'])
    offset, size = paginator(query_dct)
    if '$orderby' in query_dct:
        sort_key = query_dct['$orderby']
        cond = 'channel_type'
        if sort_key.startswith('-'):
            sort_key = sort_key[1:]
            direction = -1
        else:
            direction = 1
        items = mg.channel_status.find(query_cond).sort([(sort_key, direction), (cond, direction)]).skip(
            offset).limit(size)
    else:
        items = mg.channel_status.find(query_cond).skip(offset).limit(size)

    total_amount, avg_rate = get_aggregate_param(query_dct)
    total_count = mg.channel_status.count(query_cond)
    return items, total_count, total_amount, avg_rate


def convert_query_data_type(query_dct):
    if query_dct.get('mch_id'):
        query_dct['mch_id'] = int(query_dct['mch_id'])
    if query_dct.get('count'):
        query_dct['count'] = int(query_dct['count'])
    if query_dct.get('channel_type'):
        query_dct['channel_type'] = int(query_dct['channel_type'])
    if query_dct.get('channel_id'):
        query_dct['channel_id'] = int(query_dct['channel_id'])
    if query_dct.get('total'):
        query_dct['total'] = float(query_dct['total'])


@require_GET
@response_wrapper
@token_required
def get_channel_statistic_list(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_channel_statistic_list : %s', query_dct)
    convert_query_data_type(query_dct)
    cond = user_db.get_mchids_filter_by_user(req.user_id)
    if cond and not query_dct.get('mch_id', ''):
        query_dct['mch_id'] = cond
    items, total_count, total_charge, avg_rate = list_channel_daily_statistic(query_dct)

    resp_items = []
    for item in items:
        if item.get('service_name') and item['service_name'] == 'test':
            continue
        chn = channel_db.get_channel(item["channel_id"])
        if chn:
            settlement_method = chn.settlement_method
            chn_status = chn.status
            weight = chn.weight
        else:
            settlement_method = None
            weight = 0
            chn_status = 0
        rate = item.get('rate')
        if rate and item["total"]:
            real_income = '%.2f' % float(item["total"] * (100.0 - rate) / 100.0 / 100.0)
        else:
            real_income = u'费率或总金额为空'
        success_rate = float('%.2f' % (float(item['count']) / float(item['total_count']) * 100.0))
        manual_count = order_db.get_manual_count_by_id(item["channel_id"])
        try:
            fees = float('%.2f' % (item["total"] * rate / 10000.0))
        except:
            _LOGGER.info("Exception here , channel_id is: %s", item["channel_id"])
            fees = 0

        resp_items.append({"id": item["_id"],
                           "count": item["count"],
                           "start": item["start"],
                           "end": item["end"],
                           "channel_name": item.get('channel_name', ''),
                           "channel_id": item["channel_id"],
                           "mch_id": item["mch_id"],
                           "total": item["total"] / 100.0,
                           "rate": rate,
                           "chn_status": chn_status,
                           "settlement_method": settlement_method,
                           "real_income": real_income,
                           "total_count": item.get('total_count', 1),
                           "success_rate": success_rate,
                           "service_name": item.get('service_name', ''),
                           "manual_rate": float('%.2f' % (manual_count / float(item.get('total_count', 1)) * 100.0)),
                           "weight": weight,
                           "fees": fees})

    return {'list': resp_items, 'page': query_dct.get('$page', 1),
            'size': len(resp_items), 'total_count': total_count, 'total_charge': total_charge, 'avg_rate': avg_rate}


@mongo_wrapper
def get_data_point_by_mch_id(start, end, mch_id=None, service=None):
    group = {'_id': '$start', 'chaneL_count': {'$sum': 1}}
    for k in ('count', 'total_count', 'total',):
        group[k] = {'$sum': '$%s' % k}
    match = {'$and': [
        {'start': {'$gte': start, '$lt': end}}
    ]}
    if mch_id is not None:
        match['$and'].append({'mch_id': int(mch_id)})
    if service is not None:
        match['$and'].append({'service_name': service})
    items = mg.channel_status.aggregate([{'$match': match},
                                         {'$group': group},
                                         {'$sort': {'_id': 1}},
                                         ])
    time_list = []
    chaneL_count_list = []
    count_list = []
    total_count_list = []
    total_list = []
    success_rate_list = []
    avg_amount_list = []
    fail_count_list = []
    for item in items:
        time_list.append(item['_id'].strftime('%Y-%m-%d'))
        chaneL_count_list.append(item['chaneL_count'])
        count_list.append(item['count'])
        total_count_list.append(item['total_count'])
        total_list.append(float('%.2f' % item['total']))
        rate = float('%.2f' % (float(item['count']) / float(item['total_count']) * 100.0))
        success_rate_list.append(rate)
        avg_amount = float('%.2f' % (float(item['total']) / float(item['count']))) if item['count'] > 0 else 0
        avg_amount_list.append(avg_amount)
        fail_count_list.append(item['total_count'] - item['count'])
    return time_list, chaneL_count_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list


@require_GET
@response_wrapper
@token_required
def get_channel_daily_point(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_channel_daily_point : %s', query_dct)
    cond = user_db.get_mchids_filter_by_user(req.user_id)
    if cond and not query_dct.get('mch_id', ''):
        query_dct['mch_id'] = cond
    start = datetime.strptime(query_dct['start_at'], '%Y-%m-%d %H:%M:%S')
    end = datetime.strptime(query_dct['end_at'], '%Y-%m-%d %H:%M:%S')
    time_list, chaneL_count_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list = \
        get_data_point_by_mch_id(start, end, query_dct.get('mch_id'), query_dct.get('service'))
    return {'time_list': time_list, 'chaneL_count_list': chaneL_count_list, 'count_list': count_list,
            'total_count_list': total_count_list, 'total_list': total_list, 'success_rate_list': success_rate_list,
            'avg_amount_list': avg_amount_list, 'fail_count_list': fail_count_list}


@mongo_wrapper
def get_data_point_by_channel(channel_id, start, end, mch_id=None):
    group = {'_id': '$start', 'chaneL_count': {'$sum': 1}}
    for k in ('count', 'total_count', 'total',):
        group[k] = {'$sum': '$%s' % k}
    match = {'$and': [
        {'channel_id': int(channel_id)},
        {'start': {'$gte': start, '$lt': end}}
    ]}
    if mch_id is not None:
        match['$and'].append({'mch_id': mch_id})
    items = mg.channel_status.aggregate([{'$match': match},
                                         {'$group': group},
                                         {'$sort': {'_id': 1}},
                                         ])
    time_list = []
    count_list = []
    total_count_list = []
    total_list = []
    success_rate_list = []
    avg_amount_list = []
    fail_count_list = []
    for item in items:
        time_list.append(item['_id'].strftime('%Y-%m-%d'))
        count_list.append(item['count'])
        total_count_list.append(item['total_count'])
        total_list.append(float('%.2f' % item['total']))
        rate = float('%.2f' % (float(item['count']) / float(item['total_count']) * 100.0))
        success_rate_list.append(rate)
        avg_amount = float('%.2f' % (float(item['total']) / float(item['count']))) if item['count'] > 0 else 0
        avg_amount_list.append(avg_amount)
        fail_count_list.append(item['total_count'] - item['count'])
    return time_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list


@require_GET
@response_wrapper
@token_required
def get_single_channel_daily_point(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_single_channel_daily_point : %s', query_dct)
    cond = user_db.get_mchids_filter_by_user(req.user_id)
    if cond and not query_dct.get('mch_id', ''):
        query_dct['mch_id'] = cond
    channel_id = query_dct['channel_id']
    start = datetime.strptime(query_dct['start_at'], '%Y-%m-%d %H:%M:%S')
    end = datetime.strptime(query_dct['end_at'], '%Y-%m-%d %H:%M:%S')
    time_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list = \
        get_data_point_by_channel(channel_id, start, end, query_dct.get('mch_id'))
    return {'time_list': time_list, 'count_list': count_list,
            'total_count_list': total_count_list, 'total_list': total_list, 'success_rate_list': success_rate_list,
            'avg_amount_list': avg_amount_list, 'fail_count_list': fail_count_list}


@mongo_wrapper
def get_data_hourly_point(start, end, mch_id=None, service=None):
    group = {'_id': '$create_time', 'chaneL_count': {'$sum': 1}}
    for k in ('count', 'total_count', 'total',):
        group[k] = {'$sum': '$%s' % k}
    match = {'$and': [
        {'create_time': {'$gte': start, '$lt': end}}
    ]}
    if mch_id is not None:
        match['$and'].append({'mch_id': int(mch_id)})
    if service is not None:
        match['$and'].append({'service_name': service})

    query_params = [{'$match': match},
                    {'$group': group},
                    {'$sort': {'_id': 1}},
                    ]
    _LOGGER.info("get_data_hourly_point: %s" % query_params)
    items = mg.data_point_fix.aggregate(query_params)
    time_list = []
    chaneL_count_list = []
    count_list = []
    total_count_list = []
    total_list = []
    success_rate_list = []
    avg_amount_list = []
    fail_count_list = []
    for item in items:
        # if item['_id'].minute != 0:
        #     continue
        time_list.append(item['_id'].strftime('%Y-%m-%d %H'))
        chaneL_count_list.append(item['chaneL_count'])
        count_list.append(item['count'])
        total_count_list.append(item['total_count'])
        total_list.append(float('%.2f' % item['total']))
        rate = float('%.2f' % (float(item['count']) / float(item['total_count']) * 100.0))
        success_rate_list.append(rate)
        avg_amount = float('%.2f' % (float(item['total']) / float(item['count']))) if item['count'] > 0 else 0
        avg_amount_list.append(avg_amount)
        fail_count_list.append(item['total_count'] - item['count'])
    return time_list, chaneL_count_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list


@require_GET
@response_wrapper
@token_required
def get_mch_hourly_point(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_mch_hourly_point : %s', query_dct)
    cond = user_db.get_mchids_filter_by_user(req.user_id)
    if cond and not query_dct.get('mch_id', ''):
        query_dct['mch_id'] = cond
    start = datetime.strptime(query_dct['start_at'], '%Y-%m-%d %H:%M:%S')
    end = datetime.strptime(query_dct['end_at'], '%Y-%m-%d %H:%M:%S')
    time_list, chaneL_count_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list = \
        get_data_hourly_point(start, end, query_dct.get('mch_id'), query_dct.get('service'))
    return {'time_list': time_list, 'chaneL_count_list': chaneL_count_list, 'count_list': count_list,
            'total_count_list': total_count_list, 'total_list': total_list, 'success_rate_list': success_rate_list,
            'avg_amount_list': avg_amount_list, 'fail_count_list': fail_count_list}


@mongo_wrapper
def get_mg_single_channel_hourly_point(channel_id, start, end, mch_id=None):
    group = {'_id': '$create_time', 'chaneL_count': {'$sum': 1}}
    for k in ('count', 'total_count', 'total',):
        group[k] = {'$sum': '$%s' % k}
    match = {'$and': [
        {'create_time': {'$gte': start, '$lt': end}},
        {'channel_id': channel_id}
    ]}
    if mch_id is not None:
        match['$and'].append({'mch_id': int(mch_id)})
    items = mg.data_point_fix.aggregate([{'$match': match},
                                         {'$group': group},
                                         {'$sort': {'_id': 1}},
                                         ])
    time_list = []
    count_list = []
    total_count_list = []
    total_list = []
    success_rate_list = []
    avg_amount_list = []
    fail_count_list = []
    for item in items:
        # if item['_id'].minute != 0:
        #     continue
        time_list.append(item['_id'].strftime('%Y-%m-%d %H'))
        count_list.append(item['count'])
        total_count_list.append(item['total_count'])
        total_list.append(float('%.2f' % item['total']))
        rate = float('%.2f' % (float(item['count']) / float(item['total_count']) * 100.0))
        success_rate_list.append(rate)
        avg_amount = float('%.2f' % (float(item['total']) / float(item['count']))) if item['count'] > 0 else 0
        avg_amount_list.append(avg_amount)
        fail_count_list.append(item['total_count'] - item['count'])
    return time_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list


@require_GET
@response_wrapper
@token_required
def get_single_channel_hourly_point(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_single_channel_hourly_point : %s', query_dct)
    cond = user_db.get_mchids_filter_by_user(req.user_id)
    if cond and not query_dct.get('mch_id', ''):
        query_dct['mch_id'] = cond
    start = datetime.strptime(query_dct['start_at'], '%Y-%m-%d %H:%M:%S')
    end = datetime.strptime(query_dct['end_at'], '%Y-%m-%d %H:%M:%S')
    channel_id = int(query_dct['channel_id'])
    time_list, count_list, total_count_list, total_list, success_rate_list, avg_amount_list, fail_count_list = \
        get_mg_single_channel_hourly_point(channel_id, start, end, query_dct.get('mch_id'))
    return {'time_list': time_list, 'count_list': count_list,
            'total_count_list': total_count_list, 'total_list': total_list, 'success_rate_list': success_rate_list,
            'avg_amount_list': avg_amount_list, 'fail_count_list': fail_count_list}


def gen_file_name(channel_id, start):
    return 'MG_' + start.strftime('%Y%m%d') + '_' + str(channel_id) + '.xlsx'


@require_GET
@response_wrapper
@token_required
def get_mg_channel_day_detail(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_mg_channel_day_detail : %s', query_dct)
    channel_id = query_dct['id'].split('_')[0]
    the_day = datetime.strptime(query_dct['id'].split('_')[1], '%Y%m%d')
    if the_day.day == local_now().day or (datetime.now() - the_day).days > 30:
        raise ParamError('Today and 30 days ago have no record')
    file_url = '/export_data/' + gen_file_name(channel_id, the_day)
    _LOGGER.info('get_mg_channel_day_detail file_url : %s', file_url)
    return {'url': file_url}


@mongo_wrapper
def get_data_point_by_mch_id_pie(start, end, mch_id=None, service=None):
    group = {'_id': '$start'}
    for k in ('count', 'total_count', 'total',):
        group[k] = {'$sum': '$%s' % k}
    match = {'$and': [
        {'start': {'$gte': start, '$lt': end}}
    ]}
    if mch_id is not None:
        match['$and'].append({'mch_id': int(mch_id)})
    if service is not None:
        match['$and'].append({'service_name': service})
    items = mg.channel_status.aggregate([{'$match': match},
                                         {'$sort': {'_id': 1}},
                                         ])

    data = []
    for item in items:
        x = 0
        if 'total' in item and item['total']:
            x = float('%.2f' % item['total'])
        data.append({'service': item['service_name'], 'amount': x})
    return data


@require_GET
@response_wrapper
@token_required
def get_channel_daily_point_pie(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_channel_daily_point : %s', query_dct)
    cond = user_db.get_mchids_filter_by_user(req.user_id)
    if cond and not query_dct.get('mch_id', ''):
        query_dct['mch_id'] = cond
    start = datetime.strptime(query_dct['start_at'], '%Y-%m-%d %H:%M:%S')
    end = datetime.strptime(query_dct['end_at'], '%Y-%m-%d %H:%M:%S')
    data = get_data_point_by_mch_id_pie(start, end, query_dct.get('mch_id'), query_dct.get('service'))
    return {'data': data}


@mongo_wrapper
def get_data_point_by_mch_id_pie_hourly(start, end, mch_id=None, service=None):
    group = {'_id': '$start'}
    for k in ('count', 'total_count', 'total',):
        group[k] = {'$sum': '$%s' % k}
    match = {'$and': [
        {'start': {'$gte': start, '$lt': end}}
    ]}
    if mch_id is not None:
        match['$and'].append({'mch_id': int(mch_id)})
    if service is not None:
        match['$and'].append({'service_name': service})
    items = mg.channel_status.aggregate([{'$match': match},
                                         {'$sort': {'_id': 1}},
                                         ])
    data = []
    for item in items:
        if item['_id'].minute != 0:
            continue
        x = 0
        if 'total' in item and item['total']:
            x = float('%.2f' % item['total'])
        data.append({'service': item['service_name'], 'amount': x})
    return data


@require_GET
@response_wrapper
@token_required
def get_channel_hourly_point_pie(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_channel_daily_point : %s', query_dct)
    cond = user_db.get_mchids_filter_by_user(req.user_id)
    if cond and not query_dct.get('mch_id', ''):
        query_dct['mch_id'] = cond
    start = datetime.strptime(query_dct['start_at'], '%Y-%m-%d %H:%M:%S')
    end = datetime.strptime(query_dct['end_at'], '%Y-%m-%d %H:%M:%S')
    data = get_data_point_by_mch_id_pie(start, end, query_dct.get('mch_id'), query_dct.get('service'))
    return {'data': data}


@require_GET
@response_wrapper
@token_required
def get_reconciliation_statement(req):
    query_dct = req.GET.dict()
    _LOGGER.info('get_reconciliation_statement : %s', query_dct)

    if query_dct.get('created_at', None) is None and query_dct.get('updated_at', None) is None:
        tmp_time = datetime.utcnow() + timedelta(days=-30)
        query_dct['created_at'] = {"$gte": tmp_time.strftime("%Y-%m-%d %H:%M:%S")}

    query_dct['status'] = str(PAY_STATUS.SUCC)
    export_id = export_data_start(req.user_id, json.dumps(query_dct), 'ewqwer')
    async_job.export_data_reconciliation.delay(export_id)
    return {'export_id': export_id}
